﻿// Copyright (c) Microsoft Corporation
//
// All rights reserved. 
//
// Licensed under the Apache License, Version 2.0 (the ""License""); you may not use this file 
// except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING 
// WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY 
// OR NON-INFRINGEMENT. 
//
// See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TED.WindowsAzure.Unplugged.Mobile.RemoteStorage.AzureBlob.Storage;
using TED.WindowsAzure.Unplugged.Mobile.Sync.File;

namespace TED.WindowsAzure.Unplugged.Mobile.Tests.StateTests
{
    [TestClass]
    public class StateTest : StateTestBase
    {
        [TestInitialize]
        public void Setup()
        {
        }

        [TestCleanup]
        public async Task TearDown()
        {
            // Clean up blob storage
            await CloudBlobHelper.CleanUpBlob();

            // Clean up local FS
            await BinaryStorageHelper.CleanUp();
        }

        [TestMethod]
        public async Task InsertFile_NoOpen_NoCommit_NoPush()
        {
            var client = new UnpluggedMobileServiceClient(this.MobileServicesUri, this.MobileServicesKey);

            await client.SyncContext.InitializeAsync(new WindowsAzureBlobStorage(client));

            var folder = await
                client.SyncContext.GetSyncFolderAsync(this.FolderName);

            var file = await folder.CreateFileAsync("foo.txt");

            Assert.AreEqual(SyncFileStatus.Undefined, file.Status);

            var fileState = TableHelper.GetItems().ElementAt(0);
            Assert.AreEqual(SyncFileStatus.Undefined, fileState.Status);
            Assert.AreEqual(SyncFileStatus.Undefined, fileState.PreviousStatus);
        }

        [TestMethod]
        public async Task InsertFile_Open_NoCommit_NoPush()
        {
            var client = new UnpluggedMobileServiceClient(this.MobileServicesUri, this.MobileServicesKey);

            await client.SyncContext.InitializeAsync(new WindowsAzureBlobStorage(client));

            var folder = await
                client.SyncContext.GetSyncFolderAsync(this.FolderName);

            var file = await folder.CreateFileAsync("foo.txt");

            using (var writer = new StreamWriter((await file.OpenWriteAsync()).AsStreamForWrite()))
            {
                writer.WriteLine("foo");
                writer.Flush();
            }

            Assert.AreEqual(SyncFileStatus.Undefined, file.Status);

            var fileState = TableHelper.GetItems().ElementAt(0);
            Assert.AreEqual(SyncFileStatus.Undefined, fileState.Status);
            Assert.AreEqual(SyncFileStatus.Undefined, fileState.PreviousStatus);
        }

        [TestMethod]
        public async Task InsertFile_Open_Commit_NoPush()
        {
            var client = new UnpluggedMobileServiceClient(this.MobileServicesUri, this.MobileServicesKey);

            await client.SyncContext.InitializeAsync(new WindowsAzureBlobStorage(client));

            var folder = await
                client.SyncContext.GetSyncFolderAsync(this.FolderName);

            var file = await folder.CreateFileAsync("foo.txt");

            using (var writer = new StreamWriter((await file.OpenWriteAsync()).AsStreamForWrite()))
            {
                writer.WriteLine("foo");
                writer.Flush();
            }

            await file.CommitAsync();

            Assert.AreEqual(SyncFileStatus.Created, file.Status);

            var fileState = TableHelper.GetItems().ElementAt(0);
            Assert.AreEqual(SyncFileStatus.Created, fileState.Status);
            Assert.AreEqual(SyncFileStatus.Undefined, fileState.PreviousStatus);
        }

        [TestMethod]
        public async Task InsertFile_Open_Commit_Push()
        {
            var client = new UnpluggedMobileServiceClient(this.MobileServicesUri, this.MobileServicesKey);

            await client.SyncContext.InitializeAsync(new WindowsAzureBlobStorage(client));

            var folder = await
                client.SyncContext.GetSyncFolderAsync(this.FolderName);

            var file = await folder.CreateFileAsync("foo.txt");

            using (var writer = new StreamWriter((await file.OpenWriteAsync()).AsStreamForWrite()))
            {
                writer.WriteLine("foo");
                writer.Flush();
            }

            Assert.AreEqual(SyncFileStatus.Undefined, file.Status);

            await file.CommitAsync();
            
            Assert.AreEqual(SyncFileStatus.Created, file.Status);

            var beforePushFileState = TableHelper.GetItems().ElementAt(0);
            Assert.AreEqual(SyncFileStatus.Created, beforePushFileState.Status);
            Assert.AreEqual(SyncFileStatus.Undefined, beforePushFileState.PreviousStatus);

            await folder.PushAsync();

            file = await folder.LookupAsync("foo.txt");

            Assert.AreEqual(SyncFileStatus.AsServer, file.Status);

            var afterPushFileState = TableHelper.GetItems().ElementAt(0);
            Assert.AreEqual(SyncFileStatus.AsServer, afterPushFileState.Status);
            Assert.AreEqual(SyncFileStatus.Created, afterPushFileState.PreviousStatus);
        }

        [TestMethod]
        public async Task InsertFile_Undo()
        {
            var client = new UnpluggedMobileServiceClient(this.MobileServicesUri, this.MobileServicesKey);

            await client.SyncContext.InitializeAsync(new WindowsAzureBlobStorage(client));

            var folder = await
                client.SyncContext.GetSyncFolderAsync(this.FolderName);

            var file = await folder.CreateFileAsync("foo.txt");

            await folder.UndoAsync(file);

            Assert.IsNull(await folder.LookupAsync("foo.txt"));
            Assert.AreEqual(0, TableHelper.GetCount());
        }

        [TestMethod]
        public async Task InsertMultipleFiles_Cancel()
        {
            var client = new UnpluggedMobileServiceClient(this.MobileServicesUri, this.MobileServicesKey);

            await client.SyncContext.InitializeAsync(new WindowsAzureBlobStorage(client));

            var folder = await
                client.SyncContext.GetSyncFolderAsync(this.FolderName);

            var fooFile = await folder.CreateFileAsync("foo.txt");
            var barFile = await folder.CreateFileAsync("bar.txt");

            await folder.CancelAsync();

            Assert.IsNull(await folder.LookupAsync("foo.txt"));
            Assert.IsNull(await folder.LookupAsync("bar.txt"));
            Assert.AreEqual(0, TableHelper.GetCount());
        }

        [TestMethod]
        public async Task UpdateFile_Open_NoCommit_NoPush()
        {
            var client = new UnpluggedMobileServiceClient(this.MobileServicesUri, this.MobileServicesKey);

            await client.SyncContext.InitializeAsync(new WindowsAzureBlobStorage(client));

            var folder = await
                client.SyncContext.GetSyncFolderAsync(this.FolderName);

            var file = await folder.CreateFileAsync("foo.txt");

            using (var writer = new StreamWriter((await file.OpenWriteAsync()).AsStreamForWrite()))
            {
                writer.WriteLine("foo");
                writer.Flush();
            }

            await file.CommitAsync();
            await folder.PushAsync();

            file = await folder.LookupAsync("foo.txt");

            using (var writer = new StreamWriter((await file.OpenWriteAsync()).AsStreamForWrite()))
            {
                writer.WriteLine("bar");
                writer.Flush();
            }

            Assert.AreEqual(SyncFileStatus.Undefined, file.Status);

            var fileState = TableHelper.GetItems().ElementAt(0);
            Assert.AreEqual(SyncFileStatus.Undefined, fileState.Status);
            Assert.AreEqual(SyncFileStatus.AsServer, fileState.PreviousStatus);
        }

        [TestMethod]
        public async Task UpdateFile_Open_Commit_NoPush()
        {
            var client = new UnpluggedMobileServiceClient(this.MobileServicesUri, this.MobileServicesKey);

            await client.SyncContext.InitializeAsync(new WindowsAzureBlobStorage(client));

            var folder = await
                client.SyncContext.GetSyncFolderAsync(this.FolderName);

            var file = await folder.CreateFileAsync("foo.txt");

            using (var writer = new StreamWriter((await file.OpenWriteAsync()).AsStreamForWrite()))
            {
                writer.WriteLine("foo");
                writer.Flush();
            }

            await file.CommitAsync();
            await folder.PushAsync();

            file = await folder.LookupAsync("foo.txt");

            using (var writer = new StreamWriter((await file.OpenWriteAsync()).AsStreamForWrite()))
            {
                writer.WriteLine("bar");
                writer.Flush();
            }

            await file.CommitAsync();

            Assert.AreEqual(SyncFileStatus.Updated, file.Status);

            var fileState = TableHelper.GetItems().ElementAt(0);
            Assert.AreEqual(SyncFileStatus.Updated, fileState.Status);
            Assert.AreEqual(SyncFileStatus.Undefined, fileState.PreviousStatus);
        }

        [TestMethod]
        public async Task UpdateFile_Open_Commit_Undo()
        {
            var client = new UnpluggedMobileServiceClient(this.MobileServicesUri, this.MobileServicesKey);

            await client.SyncContext.InitializeAsync(new WindowsAzureBlobStorage(client));

            var folder = await
                client.SyncContext.GetSyncFolderAsync(this.FolderName);

            var file = await folder.CreateFileAsync("foo.txt");

            using (var writer = new StreamWriter((await file.OpenWriteAsync()).AsStreamForWrite()))
            {
                writer.WriteLine("foo");
                writer.Flush();
            }

            await file.CommitAsync();
            await folder.PushAsync();

            file = await folder.LookupAsync("foo.txt");

            using (var writer = new StreamWriter((await file.OpenWriteAsync()).AsStreamForWrite()))
            {
                writer.WriteLine("bar");
                writer.Flush();
            }

            await file.CommitAsync();

            // the undo should not affect anything as the commit has already happened
            await folder.UndoAsync(file);

            file = await folder.LookupAsync("foo.txt");

            Assert.AreEqual(SyncFileStatus.Updated, file.Status);

            var fileState = TableHelper.GetItems().ElementAt(0);
            Assert.AreEqual(SyncFileStatus.Updated, fileState.Status);
            Assert.AreEqual(SyncFileStatus.Undefined, fileState.PreviousStatus);
        }

        [TestMethod]
        public async Task UpdateFile_Open_Commit_Push()
        {
            var client = new UnpluggedMobileServiceClient(this.MobileServicesUri, this.MobileServicesKey);

            await client.SyncContext.InitializeAsync(new WindowsAzureBlobStorage(client));

            var folder = await
                client.SyncContext.GetSyncFolderAsync(this.FolderName);

            var file = await folder.CreateFileAsync("foo.txt");

            using (var writer = new StreamWriter((await file.OpenWriteAsync()).AsStreamForWrite()))
            {
                writer.WriteLine("foo");
                writer.Flush();
            }

            await file.CommitAsync();
            await folder.PushAsync();

            file = await folder.LookupAsync("foo.txt");

            using (var writer = new StreamWriter((await file.OpenWriteAsync()).AsStreamForWrite()))
            {
                writer.WriteLine("bar");
                writer.Flush();
            }

            await file.CommitAsync();
            await folder.PushAsync();

            file = await folder.LookupAsync("foo.txt");

            Assert.AreEqual(SyncFileStatus.AsServer, file.Status);

            var afterPushFileState = TableHelper.GetItems().ElementAt(0);
            Assert.AreEqual(SyncFileStatus.AsServer, afterPushFileState.Status);
            Assert.AreEqual(SyncFileStatus.Updated, afterPushFileState.PreviousStatus);
        }

        [TestMethod]
        public async Task UpdateFile_Open_Undo()
        {
            var client = new UnpluggedMobileServiceClient(this.MobileServicesUri, this.MobileServicesKey);

            await client.SyncContext.InitializeAsync(new WindowsAzureBlobStorage(client));

            var folder = await
                client.SyncContext.GetSyncFolderAsync(this.FolderName);

            var file = await folder.CreateFileAsync("foo.txt");

            using (var writer = new StreamWriter((await file.OpenWriteAsync()).AsStreamForWrite()))
            {
                writer.WriteLine("foo");
                writer.Flush();
            }

            await file.CommitAsync();
            await folder.PushAsync();

            file = await folder.LookupAsync("foo.txt");

            using (var writer = new StreamWriter((await file.OpenWriteAsync()).AsStreamForWrite()))
            {
                writer.WriteLine("bar");
                writer.Flush();
            }

            await folder.UndoAsync(file);

            Assert.IsNotNull(await folder.LookupAsync("foo.txt"));
            Assert.AreEqual(SyncFileStatus.AsServer, file.Status);

            Assert.AreEqual(1, TableHelper.GetCount());
            var afterUndoFileState = TableHelper.GetItems().ElementAt(0);
            Assert.AreEqual(SyncFileStatus.AsServer, afterUndoFileState.Status);
            Assert.AreEqual(SyncFileStatus.AsServer, afterUndoFileState.PreviousStatus);
        }

        [TestMethod]
        public async Task UpdateMultipleFile_Open_Cancel()
        {
            var client = new UnpluggedMobileServiceClient(this.MobileServicesUri, this.MobileServicesKey);

            await client.SyncContext.InitializeAsync(new WindowsAzureBlobStorage(client));

            var folder = await
                client.SyncContext.GetSyncFolderAsync(this.FolderName);

            var fooFile = await folder.CreateFileAsync("foo.txt");

            using (var writer = new StreamWriter((await fooFile.OpenWriteAsync()).AsStreamForWrite()))
            {
                writer.WriteLine("foo");
                writer.Flush();
            }

            var barFile = await folder.CreateFileAsync("bar.txt");

            using (var writer = new StreamWriter((await barFile.OpenWriteAsync()).AsStreamForWrite()))
            {
                writer.WriteLine("bar");
                writer.Flush();
            }

            await folder.FlushAsync();
            await folder.PushAsync();

            fooFile = await folder.LookupAsync("foo.txt");

            using (var writer = new StreamWriter((await fooFile.OpenWriteAsync()).AsStreamForWrite()))
            {
                writer.WriteLine("foobar");
                writer.Flush();
            }

            barFile = await folder.LookupAsync("bar.txt");

            using (var writer = new StreamWriter((await barFile.OpenWriteAsync()).AsStreamForWrite()))
            {
                writer.WriteLine("barfoo");
                writer.Flush();
            }

            await folder.CancelAsync();

            fooFile = await folder.LookupAsync("foo.txt");
            Assert.IsNotNull(fooFile);
            Assert.AreEqual(SyncFileStatus.AsServer, fooFile.Status);

            barFile = await folder.LookupAsync("bar.txt");
            Assert.IsNotNull(barFile);
            Assert.AreEqual(SyncFileStatus.AsServer, barFile.Status);

            Assert.AreEqual(2, TableHelper.GetCount());

            var afterCancelfooFileState = TableHelper.GetItems().ElementAt(0);
            Assert.AreEqual(SyncFileStatus.AsServer, afterCancelfooFileState.Status);
            Assert.AreEqual(SyncFileStatus.AsServer, afterCancelfooFileState.PreviousStatus);

            var afterCancelbarFileState = TableHelper.GetItems().ElementAt(1);
            Assert.AreEqual(SyncFileStatus.AsServer, afterCancelbarFileState.Status);
            Assert.AreEqual(SyncFileStatus.AsServer, afterCancelbarFileState.PreviousStatus);
        }

        [TestMethod]
        public async Task DeleteFile_NoPush()
        {
            var client = new UnpluggedMobileServiceClient(this.MobileServicesUri, this.MobileServicesKey);

            await client.SyncContext.InitializeAsync(new WindowsAzureBlobStorage(client));

            var folder = await
                client.SyncContext.GetSyncFolderAsync(this.FolderName);

            var file = await folder.CreateFileAsync("foo.txt");

            using (var writer = new StreamWriter((await file.OpenWriteAsync()).AsStreamForWrite()))
            {
                writer.WriteLine("foo");
                writer.Flush();
            }

            await file.CommitAsync();
            await folder.PushAsync();

            file = await folder.LookupAsync("foo.txt");

            await file.DeleteAsync();

            Assert.AreEqual(SyncFileStatus.Deleted, file.Status);

            var fileState = TableHelper.GetItems().ElementAt(0);
            Assert.AreEqual(SyncFileStatus.Deleted, fileState.Status);
            Assert.AreEqual(SyncFileStatus.AsServer, fileState.PreviousStatus);
        }

        [TestMethod]
        public async Task DeleteFile_Push()
        {
            var client = new UnpluggedMobileServiceClient(this.MobileServicesUri, this.MobileServicesKey);

            await client.SyncContext.InitializeAsync(new WindowsAzureBlobStorage(client));

            var folder = await
                client.SyncContext.GetSyncFolderAsync(this.FolderName);

            var file = await folder.CreateFileAsync("foo.txt");

            using (var writer = new StreamWriter((await file.OpenWriteAsync()).AsStreamForWrite()))
            {
                writer.WriteLine("foo");
                writer.Flush();
            }

            await file.CommitAsync();
            await folder.PushAsync();

            file = await folder.LookupAsync("foo.txt");

            await file.DeleteAsync();

            Assert.AreEqual(SyncFileStatus.Deleted, file.Status);

            var beforePushFileState = TableHelper.GetItems().ElementAt(0);
            Assert.AreEqual(SyncFileStatus.Deleted, beforePushFileState.Status);
            Assert.AreEqual(SyncFileStatus.AsServer, beforePushFileState.PreviousStatus);

            await folder.PushAsync();

            file = await folder.LookupAsync("foo.txt");

            Assert.IsNull(file);
        }
    }
}
